/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.user.client.ui.nojs;

import java.util.ArrayList;
import java.util.List;

import com.cleangwt.bootstrap.client.style.Styles;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.DeckLayoutPanel;
import com.google.gwt.user.client.ui.SimpleLayoutPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * A widget provieds method for adding and alternating items in animation.
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 * @deprecated experimatal
 */
public class Carousel extends SimpleLayoutPanel {

  private class CarouselLayout extends DeckLayoutPanel {

    void rollingDown() {
      // remove first and put it to last
      Widget item = this.getWidget(0);
      this.getChildren().remove(0);
      this.getChildren().insert(item, getWidgetCount());
    }

    void rollingUp() {
      int last = getWidgetCount() - 1;
      // remove last and put it to first
      Widget item = this.getWidget(last);
      this.getChildren().remove(last);
      this.getChildren().insert(item, 0);
    }
  }

  private static final String DEFAULT_STYLENAME = "carousel";

  private static final String CAPTION_STYLE = "carousel-caption";
  private static final String CONTROL_STYLE = "carousel-control";
  private static final String INDICATOR_STYLE = "carousel-indicators";
  private static final String INNER_STYLE = "inner";
  private static final int DEFAULT_ROLLING_DELAY = 3000;

  private int animationDelay = DEFAULT_ROLLING_DELAY;
  private CarouselLayout deck = new CarouselLayout();
  private List<Widget> indexedItems = new ArrayList<Widget>();
  private Element indicator = Document.get().createOLElement();
  private Element next = Document.get().createAnchorElement();
  private Element prev = Document.get().createAnchorElement();
  private boolean rolling;
  private boolean rollingOnLoad;
  private Timer timer;

  /**
   * Creates a carousel.
   */
  public Carousel() {
    this(false);
  }

  /**
   * Creates a carousel with the specified animation orientation.
   * 
   * @param isVertical to determinate the animation orientation
   */
  public Carousel(boolean isVertical) {
    this(isVertical, true);
  }

  /**
   * Creates a carousel with the specified animation orientation and rolling state.
   * 
   * @param isVertical to determinate the animation orientation
   * @param rollingOnLoad whether to start rolling on load
   */
  public Carousel(boolean isVertical, boolean rollingOnLoad) {
    this.rollingOnLoad = rollingOnLoad;

    Element prevIcon = Document.get().createSpanElement();
    Element nextIcon = Document.get().createSpanElement();

    deck.setAnimationVertical(isVertical);
    setWidget(deck);
    this.getElement().appendChild(indicator);
    this.getElement().appendChild(prev);
    this.getElement().appendChild(next);
    this.setStyleName(DEFAULT_STYLENAME);
    this.sinkEvents(Event.ONCLICK);

    deck.setStyleName(INNER_STYLE);
    deck.setAnimationDuration(500);

    indicator.setClassName(INDICATOR_STYLE);

    prev.setClassName(CONTROL_STYLE);
    prev.setAttribute("href", "#");
    prev.addClassName("left");
    prev.appendChild(prevIcon);

    prevIcon.setClassName(Styles.ICON_PREV);
    nextIcon.setClassName(Styles.ICON_NEXT);

    next.setClassName(CONTROL_STYLE);
    next.setAttribute("href", "#");
    next.addClassName("right");
    next.appendChild(nextIcon);
  }

  /**
   * Adds a item with caption.
   * 
   * @param caption the caption about item
   * @param item the item to be shown alternatively.
   */
  public void addItem(String caption, Widget item) {
    SimplePanel wraper = new SimplePanel();
    Element captionElement = Document.get().createDivElement();

    wraper.setWidget(item);
    wraper.getElement().appendChild(captionElement);

    captionElement.setClassName(CAPTION_STYLE);
    captionElement.setInnerHTML(caption);
    item.setSize("100%", "100%");

    indexedItems.add(wraper);
    deck.add(wraper);
    indicator.appendChild(Document.get().createLIElement());
  }

  @Override
  public void onBrowserEvent(Event event) {
    int type = event.getTypeInt();
    if (type == Event.ONCLICK) {
      if (Element.is(event.getEventTarget())) {
        Element e = Element.as(event.getEventTarget());
        boolean action = false;
        if (prev.isOrHasChild(e)) {
          action = true;
          stopRolling();
          this.showPrev();
        } else if (next.isOrHasChild(e)) {
          action = true;
          stopRolling();
          this.showNext();
        } else {
          for (int i = 0; i < indicator.getChildCount(); i++) {
            if (e == indicator.getChild(i)) {
              action = true;
              show(i);
              break;
            }
          }
        }

        if (action) {
          // restart rolling
          startRolling();
          DOM.eventPreventDefault(event);
        } else {
          super.onBrowserEvent(event);
        }

      }
    }
  }

  /**
   * Sets the animation delay.
   * 
   * @param delay the mill delay
   */
  public void setAnimationDelay(int delay) {
    this.animationDelay = delay;
  }

  /**
   * Shows item of the specified index.
   * 
   * @param index the index
   */
  public void show(int index) {
    showItem(indexedItems.get(index));
  }

  /**
   * Starts rolling.
   */
  public void startRolling() {
    if (timer == null) {
      timer = new Timer() {

        @Override
        public void run() {
          if (rolling) {
            showNext();
          }
        }
      };
    }
    rolling = true;
    timer.cancel();
    timer.scheduleRepeating(animationDelay);
  }

  /**
   * Stops rolling.
   */
  public void stopRolling() {
    rolling = false;
    timer.cancel();
  }

  @Override
  protected void onLoad() {
    super.onLoad();
    this.show(0);
    if (rollingOnLoad) {
      startRolling();
    }
  }

  @Override
  protected void onUnload() {
    super.onUnload();
    stopRolling();
  }

  /**
   * Shows the specified item.
   * 
   * @param item the item
   */
  protected void showItem(Widget item) {
    deck.showWidget(item);
    int index = indexedItems.indexOf(item);
    for (int i = 0; i < indicator.getChildCount(); i++) {
      Element e = indicator.getChild(i).cast();
      if (i == index) {
        e.addClassName("active");
      } else {
        e.removeClassName("active");
      }
    }
  }

  /**
   * Shows the next item.
   */
  protected void showNext() {
    int index = deck.getVisibleWidgetIndex();
    if (index == deck.getWidgetCount() - 1) {
      deck.rollingDown();
      showItem(deck.getWidget(deck.getWidgetCount() - 1));
    } else {
      showItem(deck.getWidget(index + 1));
    }
  }

  /**
   * Shows the previous item.
   */
  protected void showPrev() {
    int index = deck.getVisibleWidgetIndex();
    if (index <= 0) {
      deck.rollingUp();
      showItem(deck.getWidget(0));
    } else {
      showItem(deck.getWidget(index - 1));
    }
  }

}
